package cn.net.asiacom.extrator;

import cn.net.asiacom.entity.Invoice;
import cn.net.asiacom.entity.Original;
import cn.net.asiacom.extrator.extra.PDFKeyWordPosition;
import cn.net.asiacom.extrator.extra.Position;
import cn.net.asiacom.utils.ReceiptFileUtil;
import cn.net.asiacom.utils.StrUtil;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.text.PDFTextStripperByArea;

import java.awt.*;
import java.io.File;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static cn.net.asiacom.config.RegexEnum.*;

/**
 * 发票提取器
 */
public class InvoiceExtractor {

    private InvoiceExtractor(){}

    /**
     * 从本地文件或URL中提取发票信息。
     */
    public synchronized static Invoice extractInvoice(String filePath) {
        return extractInvoice(new File(filePath));
    }

    /**
     * 从本地文件或URL中提取发票信息。
     */
    public synchronized static Invoice extractInvoice(File pdfFile) {
        Original original = ReceiptFileUtil.processFile(pdfFile);
        String ticketETicketNumber = getTicketETicketNumber(StrUtil.replace(original.getFullText()));
        if (StrUtil.isNotBlank(ticketETicketNumber)) {
            ReceiptFileUtil.close(original.getDoc());
            throw new RuntimeException("无效电子发票");
        }
        Invoice invoice = new Invoice();
        getInvoiceNumber(StrUtil.replace(original.getFullText()), invoice);
        if (StrUtil.isBlank(invoice.getNumber())) {
            ReceiptFileUtil.close(original.getDoc());
            throw new RuntimeException("非电子发票");
        }
        getTitleAndType(StrUtil.replace(original.getFullText()), invoice);
        getInvoiceDate(StrUtil.replace(original.getFullText()), invoice);
        getDrawer(StrUtil.replace(original.getFullText()), invoice);
        getBuyerInfo(original.getDoc(), original.getPage(), StrUtil.replace(original.getFullText()), invoice);
        getAmountAndTax(StrUtil.replace(original.getFullText()), invoice);
        ReceiptFileUtil.close(original.getDoc());
        return invoice;
    }

    /**
     * 提取发票代码
     *
     * @param allText 发票文本
     * @return 发票信息
     */
    private synchronized static String getCode(String allText) {
        String code = null;
        Pattern pattern = Pattern.compile(REGEX_INVOICE_CODE.getRegex());
        Matcher matcher = pattern.matcher(allText);
        if (matcher.find()) {
            code = matcher.group("code");
        }
        if (StrUtil.isBlank(code)) {
            Matcher matcher4Replace = Pattern.compile(REGEX_INVOICE_CODE_1.getRegex()).matcher(allText);
            while (matcher4Replace.find()) {
                String firstCode = matcher4Replace.group("code");
                String secondCode = matcher4Replace.group(2);
                code = secondCode + firstCode;
            }
        }
        return code;
    }

    /**
     * 提取电子发票号 电子客票号
     * @param allText 发票文本
     * @return 发票信息
     */
    private synchronized static String getTicketETicketNumber(String allText) {
        String eTicketNumber = null;
        Pattern pattern = Pattern.compile(TICKET_INVOICE_E_TICKET_NUMBER.getRegex());
        Matcher ticketNumber = pattern.matcher(allText);
        if (ticketNumber.find()) {
            eTicketNumber = (ticketNumber.group(1));
        }
        return eTicketNumber;
    }
    /**
     * 提取发票标题和类型
     * @param allText 发票文本
     * @return 发票信息
     */
    private synchronized static Invoice getTitleAndType(String allText,Invoice invoice) {
        Pattern pattern = Pattern.compile(REGEX_INVOICE_TYPE_P.getRegex());
        Matcher m00 = pattern.matcher(allText);
        Pattern type01Pattern = Pattern.compile(REGEX_INVOICE_TYPE_Z.getRegex());
        Matcher m01 = type01Pattern.matcher(allText);
        if (m00.find()) {
            if (Objects.nonNull(m00.group("p"))) {
                invoice.setTitle(m00.group("p").replaceAll("\\(", "").replaceAll(REGEX_INVOICE_TYPE_AUX.getRegex(), "") + "通发票");
            } else {
                invoice.setTitle("电子发票");
            }
            if (null == invoice.getType()) {
                invoice.setType("普通发票");
            }
        } else if (m01.find()) {
            if (Objects.nonNull(m01.group("p"))) {
                invoice.setTitle(m01.group("p").replaceAll("\\(", "").replaceAll(REGEX_INVOICE_TYPE_AUX.getRegex(), "") + "用发票");
            } else {
                invoice.setTitle("电子发票");
            }
            if (null == invoice.getType()) {
                invoice.setType("专用发票");
            }
        }
        return invoice;
    }

    /**
     * 提取发票号码
     * @param allText 发票文本
     * @return 发票信息
     */
    private synchronized static Invoice getInvoiceNumber(String allText,Invoice invoice) {
        Pattern pattern = Pattern.compile(REGEX_INVOICE_NUMBER.getRegex());
        Matcher m00 = pattern.matcher(allText);
        if (m00.find()) {
            invoice.setNumber(m00.group("number"));
        } else {
            Pattern pattern1 = Pattern.compile(REGEX_INVOICE_NUMBER_1.getRegex());
            Matcher m01 = pattern1.matcher(allText);
            if (m01.find()) {
                invoice.setNumber(m01.group("number"));
            }
        }
        if (StrUtil.isBlank(invoice.getNumber())) {
            Pattern pattern2 = Pattern.compile(REGEX_INVOICE_NUMBER_2.getRegex());
            Matcher m02 = pattern2.matcher(allText);
            if (m02.find()) {
                invoice.setNumber(m02.group("number"));
            }
        }
        return invoice;
    }

    /**
     * 提取发票日期
     * @param allText 发票文本
     * @return 发票信息
     */
    private synchronized static Invoice getInvoiceDate(String allText,Invoice invoice) {
        Pattern pattern = Pattern.compile(REGEX_INVOICE_DATE.getRegex());
        Matcher m00 = pattern.matcher(allText);
        if (m00.find()) {
            String rawDate = m00.group("date");
            SimpleDateFormat inputDateFormat = new SimpleDateFormat("yyyy年MM月dd日");
            SimpleDateFormat outputDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            Date parsedDate = null;
            try {
                parsedDate = inputDateFormat.parse(rawDate);
            } catch (ParseException e) {
                throw new RuntimeException("无法解析日期：" + rawDate);
            }
            String formattedDate = outputDateFormat.format(parsedDate);
            invoice.setDate(formattedDate);
        }
        return invoice;
    }

    /**
     * 提取开票人
     * @param allText 发票文本
     * @return 发票信息
     */
    private synchronized static Invoice getDrawer(String allText,Invoice invoice) {
        Pattern pattern = Pattern.compile(REGEX_INVOICE_DRAWER.getRegex());
        Matcher m00 = pattern.matcher(allText);
        while (m00.find()) {
            String name = m00.group("drawer");
            if (StrUtil.isNotBlank(name)) {
                invoice.setDrawer(name);
            }
        }
        if (StrUtil.isBlank(invoice.getDrawer())) {
            Pattern pattern1 = Pattern.compile(REGEX_INVOICE_DRAWER_1.getRegex());
            Matcher m01 = pattern1.matcher(allText);
            while (m01.find()) {
                String name = m01.group("drawer");
                if (StrUtil.isNotBlank(name)) {
                    invoice.setDrawer(name);
                }
            }
        }
        return invoice;
    }

    /**
     * 提取购买方名称和纳税人识别号, 销售方名称和纳税人识别号
     * @param allText 发票文本
     * @return 发票信息
     */
    private synchronized static Invoice getBuyerInfo(PDDocument doc,PDPage page, String allText,Invoice invoice) {
        String code = getCode(allText);
        if (StrUtil.isNotBlank(code)) {
            // 获取页面宽度
            int pageWidth = Math.round(page.getCropBox().getWidth());
            try {
                PDFKeyWordPosition kwp = new PDFKeyWordPosition();
                Map<String, List<Position>> positionListMap = kwp.getCoordinate(Arrays.asList("机器编号", "税率", "价税合计", "合计", "开票日期", "规格型号", "车牌号", "开户行及账号", "密", "码", "区"), doc);
                PDFTextStripperByArea stripper = new PDFTextStripperByArea();
                stripper.setSortByPosition(true);
                PDFTextStripperByArea detailStripper = new PDFTextStripperByArea();
                detailStripper.setSortByPosition(true);
                if (positionListMap == null) {
                    return invoice;
                }
                {
                    Position machineNumber;
                    if (positionListMap.containsKey("机器编号") && positionListMap.get("机器编号").size() > 0) {
                        machineNumber = positionListMap.get("机器编号").get(0);
                    } else {
                        machineNumber = positionListMap.get("开票日期").get(0);
                        machineNumber.setY(machineNumber.getY() + 30);
                    }
                    Position taxRate = positionListMap.get("税率").get(0);
                    Position totalAmount = positionListMap.get("价税合计").get(0);
                    Position amount = positionListMap.get("合计").get(0);
                    Position model = null;
                    if (!positionListMap.get("规格型号").isEmpty()) {
                        model = positionListMap.get("规格型号").get(0);
                    } else {
                        model = positionListMap.get("车牌号").get(0);
                        model.setX(model.getX() - 15);
                    }
                    List<Position> account = positionListMap.get("开户行及账号");
                    Position buyer;
                    Position seller;
                    if (account.size() < 2) {
                        buyer = new Position(51, 122);
                        seller = new Position(51, 341);
                    } else {
                        buyer = account.get(0);
                        seller = account.get(1);
                    }

                    int maqX = 370;

                    List<Position> mi = positionListMap.get("密");
                    List<Position> ma = positionListMap.get("码");
                    List<Position> qu = positionListMap.get("区");

                    for (Position position : mi) {
                        float x1 = position.getX();
                        for (Position value : ma) {
                            float x2 = value.getX();
                            if (Math.abs(x1 - x2) < 5) {
                                for (Position item : qu) {
                                    float x3 = item.getX();
                                    if (Math.abs(x2 - x3) < 5) {
                                        maqX = Math.round((x1 + x2 + x3) / 3);
                                    }
                                }
                            }
                        }
                    }

                    {
                        int x = Math.round(model.getX()) - 13;
                        int y = Math.round(taxRate.getY()) + 5;
                        int h = Math.round(amount.getY()) - Math.round(taxRate.getY()) - 25;
                        detailStripper.addRegion("detail", new Rectangle(0, y, pageWidth, h));
                        stripper.addRegion("detailName", new Rectangle(0, y, x, h));
                        stripper.addRegion("detailPrice", new Rectangle(x, y, pageWidth, h));
                    }

                    {
                        int x = Math.round(buyer.getX()) - 15;
                        int y = Math.round(machineNumber.getY()) + 10;
                        int w = maqX - x - 5;
                        int h = Math.round(buyer.getY()) - y + 20;
                        stripper.addRegion("buyer", new Rectangle(x, y, w, h));
                    }
                    {
                        int x = Math.round(seller.getX()) - 15;
                        int y = Math.round(totalAmount.getY()) + 10;
                        int w = maqX - x - 5;
                        int h = Math.round(seller.getY()) - y + 20;
                        stripper.addRegion("seller", new Rectangle(x, y, w, h));
                    }
                }

                stripper.extractRegions(page);
                detailStripper.extractRegions(page);

                {
                    String buyer = StrUtil.replace(stripper.getTextForRegion("buyer"));
                    Pattern pattern = Pattern.compile(REGEX_INVOICE_BUYER.getRegex());
                    Matcher matcher = pattern.matcher(buyer);
                    while (matcher.find()) {
                        if (matcher.group("name") != null) {
                            invoice.setBuyerName(matcher.group("name"));
                        } else if (matcher.group("code") != null) {
                            invoice.setBuyerCode(matcher.group("code"));
                        }
                    }
                }

                {
                    String seller = StrUtil.replace(stripper.getTextForRegion("seller"));
                    Pattern pattern = Pattern.compile(REGEX_INVOICE_BUYER.getRegex());
                    Matcher matcher = pattern.matcher(seller);
                    while (matcher.find()) {
                        if (matcher.group("name") != null) {
                            if (matcher.group("name").contains("出有限公司")){
                                String replace = matcher.group("name").replace("出有限公司", "出版社有限公司");
                                invoice.setSellerName(replace);
                            } else if (matcher.group("name").contains("贸有公司")) {
                                String replace = matcher.group("name").replace("贸有公司", "贸易有限公司");
                                invoice.setSellerName(replace);
                            } else {
                                invoice.setSellerName(matcher.group("name"));
                            }
                        } else if (matcher.group("code") != null) {
                            invoice.setSellerCode(matcher.group("code"));
                        }
                    }
                }

                if (invoice.getSellerName() == null){
                    Pattern patternReplace = Pattern.compile(REGEX_INVOICE_BUYER_1.getRegex());
                    Matcher matcherReplace = patternReplace.matcher(allText);
                    while (matcherReplace.find()) {
                        if (matcherReplace.group("name") != null) {
                            if (!matcherReplace.group("name").contains("学院") || !matcherReplace.group("name").contains("大学")){
                                if (matcherReplace.group("name").contains("出有限公司")){
                                    String replace = matcherReplace.group("name").replace("出有限公司", "出版社有限公司");
                                    invoice.setSellerName(replace);
                                } else if (matcherReplace.group("name").contains("贸有公司")) {
                                    String replace = matcherReplace.group("name").replace("贸有公司", "贸易有限公司");
                                    invoice.setSellerName(replace);
                                } else {
                                    invoice.setSellerName(matcherReplace.group("name"));
                                }
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            Pattern pattern = Pattern.compile(REGEX_INVOICE_BUYER_2.getRegex());
            Matcher matcher = pattern.matcher(allText);
            while (matcher.find()) {
                if (matcher.group("name") != null) {
                    invoice.setSellerName(matcher.group("name"));
                }
            }

            pattern = Pattern.compile(REGEX_INVOICE_BUYER_3.getRegex());
            matcher = pattern.matcher(allText);
            int i = 0;
            while (matcher.find()) {
                if (StrUtil.isNotBlank(matcher.group("name"))) {
                    invoice.setBuyerName(matcher.group("name").replaceAll("销名称",""));
                }
                // 获取购买方纳税人识别号/销售方纳税人识别号
                if (StrUtil.isNotBlank(matcher.group("code")) && i == 0) {
                    i = 1;
                    invoice.setBuyerCode(matcher.group("code"));
                } else if (StrUtil.isNotBlank(matcher.group("code")) && i == 1){
                    i = 0;
                    invoice.setSellerCode(matcher.group("code"));
                }
            }
        }
        return invoice;
    }

    /**
     * 提取合计金额和税额
     * @param allText 发票文本
     * @return 发票信息
     */
    private synchronized static Invoice getAmountAndTax(String allText,Invoice invoice) {
        // 金额、税额提取正则，匹配形如 "合计¥?金额¥?税额" 的文本
        Pattern pattern = Pattern.compile(REGEX_INVOICE_AMOUNT.getRegex());
        Matcher matcher = pattern.matcher(allText);
        if (matcher.find()) {
            try {
                invoice.setAmount(new BigDecimal(matcher.group("amount")));
            } catch (Exception e) {}
            try {
                invoice.setTaxAmount(new BigDecimal(matcher.group("taxAmount")));
            } catch (Exception e) {
                invoice.setTaxAmount(new BigDecimal(0));
            }
        }
        pattern = Pattern.compile(REGEX_INVOICE_AMOUNT_B.getRegex());
        matcher = pattern.matcher(allText);
        if (matcher.find()) {
            String group = matcher.group(1);
            String group1 = matcher.group(2);
            try {
                invoice.setAmount(new BigDecimal(group));
            } catch (Exception e) {}
            try {
                invoice.setTaxAmount(new BigDecimal(group1));
            } catch (Exception e) {
                invoice.setTaxAmount(new BigDecimal(0));
            }
        }

        // 备用1：获取合计金额和税额
        if (Objects.isNull(invoice.getAmount())) {
            pattern = Pattern.compile(REGEX_INVOICE_AMOUNT_1.getRegex());
            matcher = pattern.matcher(allText);
            if (matcher.find()) {
                try {
                    invoice.setAmount(new BigDecimal(matcher.group("amount")));
                } catch (Exception e) {}
                try {
                    invoice.setTaxAmount(new BigDecimal(matcher.group("taxAmount")));
                } catch (Exception e) {
                    invoice.setTaxAmount(new BigDecimal(0));
                }
            }
        }
        // 备用2：获取合计金额和税额
        if (Objects.isNull(invoice.getAmount())) {
            pattern = Pattern.compile(REGEX_INVOICE_AMOUNT_2.getRegex());
            matcher = pattern.matcher(allText);
            if (matcher.find()) {
                try {
                    invoice.setAmount(new BigDecimal(matcher.group("amount")));
                } catch (Exception e) {
                    invoice.setAmount(new BigDecimal(0));
                }
                try {
                    invoice.setTaxAmount(new BigDecimal(matcher.group("taxAmount")));
                } catch (Exception e) {
                    invoice.setTaxAmount(new BigDecimal(0));
                }
            }
        }
        invoice.setTotalAmount(invoice.getAmount().add(invoice.getTaxAmount()));

        pattern = Pattern.compile(REGEX_INVOICE_AMOUNT_C.getRegex());
        matcher = pattern.matcher(allText);
        if (matcher.find()) {
            invoice.setTotalAmountString(matcher.group("amountString"));
            try {
                invoice.setTotalAmount(new BigDecimal(matcher.group("amount")));
            } catch (Exception e) {
                invoice.setTotalAmount(new BigDecimal(0));
            }
        }
        return invoice;
    }

}
